-- SlidePanel
-- created by dengc
-- 滑动选择工具函数

local isMoveing = false;
local selectIndex = 1;
local panel;
local bottomPanel;
local itemList = {};
local initItem;
local itemNode;
local clickCallFunc;
local moveCallFunc;

-- 计算index代表的分组item和指定的分组的距离
local function calcDistance(index, targetIndex)
    local selectIndex = targetIndex or selectIndex;

    local fristIndex = index - selectIndex
    local secondIndex = index + #itemList - selectIndex;
    local thridIndex = index - #itemList - selectIndex;

    local minDistance = math.min(math.abs(fristIndex), math.abs(secondIndex), math.abs(thridIndex));
    if minDistance == math.abs(fristIndex) then
        return fristIndex;
    elseif minDistance == math.abs(secondIndex) then
        return secondIndex;
    elseif minDistance == math.abs(thridIndex) then
        return thridIndex;
    end
end

-- 计算Index代表的分组item在指定的分组为选中分组情况下的坐标
local function calcItemPos(index, targetIndex)
    local selectIndex = targetIndex or selectIndex;
    local distance = calcDistance(index, selectIndex);
    local origin = { ["x"] = panel:getContentSize().width / 2, ["y"] = panel:getContentSize().height / 2, };

    return cc.p(origin.x + distance * itemWidthGap, origin.y - 20 + math.abs(distance) * itemheightGap);
end

-- 计算Index代表的分组item在指定的分组为选中分组情况下的比例
local function calcItemScale(index, targetIndex)
    local selectIndex = targetIndex or selectIndex;
    local distance = calcDistance(index, selectIndex);

    return math.pow(0.8, math.abs(distance)) * 0.9;
end

-- 遍历一个节点的所有子节点并设置它们的透明度
local function setOpacity(node, opacity)
    local childs = node:getChildren();
    if #childs == 0 then
        node:setOpacity(opacity);
    else
        for _, child in pairs(childs) do
            setOpacity(child, opacity);
        end
    end
end

-- 绘制整理列表内的物品
local function redrawItems()
    -- 在Panel绘制所有物品
    for i = 1, #itemList do
        local child = findChildByName(bottomPanel, "item" .. i);

        child:setPosition(calcItemPos(i));
        child:setScale(calcItemScale(i));

        -- 绘制下内容
        if not child.hasInit then
            if initItem then
                initItem(child, i);
                child.hasInit = true;
            end
        end

        -- 当前选中的分组不显示，其他的全部显示
        if i == selectIndex or math.abs(calcDistance(i)) > 2 then
            child:setVisible(false);
        else
            child:setVisible(true);
        end

       -- setOpacity(child, 100);
    end

    -- 绘制panel中选中的飞艇
    local child = findChildByName(panel, "selectItem");

    child:setPosition(calcItemPos(selectIndex));
    child:setScale(calcItemScale(selectIndex));

    -- 绘制下内容
    if initItem then
        initItem(child, selectIndex, true);
    end

    -- 恢复下飞艇的y坐标值
    if not child.initPosY then
        child.initPosY = child:getPositionY();
    end
    child:setPositionY(child.initPosY);
end

-- 移动
local function moveItems(direction, callback)
    local delay = 0.2;

    for i = 1, #itemList do
        local child;
        if i == selectIndex then
            child = findChildByName(panel, "selectItem");
        else
            child = findChildByName(bottomPanel, "item" .. i);
        end

        -- 计算这次移动后，选中的item会变成哪一个
        local nextIndex;
        if direction == 1 then
            nextIndex = selectIndex + 1;
            if nextIndex > #itemList then
                nextIndex = 1;
            end
        elseif direction == -1 then
            nextIndex = selectIndex - 1;
            if nextIndex <= 0 then
                nextIndex = #itemList;
            end
        end

        -- 计算child要移动到哪个位置，比例又是多少
        local curPos = calcItemPos(i);
        local desPos = calcItemPos(i, nextIndex);
        local movePos = cc.p(direction * -1 * itemWidthGap, desPos.y - curPos.y);

        -- 只有当分组只有4组的时候需要这个特殊处理
        -- 当只有4个分组的情况下无法满足往任意一方移动均可保证另一方向有个分组可以移动进来
        if #itemList == 4 then
            if direction == 1 then
                if i == selectIndex - 2 or i == selectIndex + 2 then
                    child:setPositionX(desPos.x + itemWidthGap);
                end
            else
                if i == selectIndex - 2 or i == selectIndex + 2 then
                    child:setPositionX(desPos.x - itemWidthGap);
                end
            end
        end

        local moveBy = cc.MoveBy:create(delay, movePos);
        local scaleTo = cc.ScaleTo:create(delay, calcItemScale(i, nextIndex));
        local callFunc = cc.CallFunc:create(function()
            child:setVisible(true);
        end);
        child:runAction(cc.Sequence:create(callFunc, cc.Spawn:create(moveBy, scaleTo)));
    end

    performWithDelay(panel, callback, delay);
end

local function onPanelClick(sender,eventType)
    if isMoveing then
        return;
    end

    local child = findChildByName(panel, "selectItem");
    local scale = calcItemScale(selectIndex);
    if eventType == ccui.TouchEventType.began then
        sender.initMoveX = nil;
        if sender.notScale ~= true then
            child:runAction(cc.ScaleTo:create(0.1, scale-0.03));
        end
    elseif eventType == ccui.TouchEventType.ended then
        sender.initMoveX = nil;
        child:runAction(cc.ScaleTo:create(0.1, scale));

        -- 如果sender自带callback，执行sender的callback
        if "function" == type(sender.childClickFunc) then
            sender.childClickFunc(sender.classId);
        else
            -- 点击回调
            if clickCallFunc then
                clickCallFunc(child);
            end
        end


    elseif eventType == ccui.TouchEventType.canceled then
        child:runAction(cc.ScaleTo:create(0.1, scale));
    elseif eventType == ccui.TouchEventType.moved then
        local movePos = sender:getTouchMovePosition();
        if not sender.initMoveX then
            sender.initMoveX = movePos.x;
        end

        local direction;
        local moveLen = AlignM.frameSize.width / 10;
        if movePos.x - sender.initMoveX > moveLen then
            -- 往右滑动
            direction = -1;
        elseif movePos.x - sender.initMoveX < -1 * moveLen then
            -- 往左滑动
            direction = 1;
        else
            return;
        end

        -- 先停止点击事件的回调
        isMoveing = true;
        sender.initMoveX = movePos.x;

        local function callback()
            isMoveing = false;
            if direction == 1 then
                selectIndex = selectIndex + 1;
                if selectIndex > #itemList then
                    selectIndex = 1;
                end
            elseif direction == -1 then
                selectIndex = selectIndex - 1;
                if selectIndex <= 0 then
                    selectIndex = #itemList;
                end
            end

            -- 重绘下
            redrawItems();

            -- 移动回调
            if moveCallFunc then moveCallFunc(child, selectIndex); end
        end

        -- 移动
        moveItems(direction, callback);
    end
end

local function initGrid()
    local widget = replaceNodeWithWidget(itemNode);

    for i = 1, #itemList do
        local child = widget:clone();
        child:setName("item" .. i);
        bottomPanel:addChild(child);
    end

    local child = widget:clone();
    child:setName("selectItem");
    panel:addChild(child);
end

-- -------------------------------------------------------------------------------------

-- 创建一个鼠标选择滑动的容器
-- @selectPanel  基础容器
-- @otherPanel   底部容器
-- @node         模板节点
-- @list         需要绘制的物品列表
-- @itemWidth    绘制物品的宽度
-- @itemHeight   绘制物品的高度
-- @initFunc     绘制单个物品的函数
-- @clickFunc    点击回调
-- @moveFunc     移动回调
function createSlidePanel(selectPanel, otherPanel, node, list, itemWidth, itemHeight, initFunc, clickFunc, moveFunc)
    -- 记录下必须的数据
    itemList = list;
    initItem = initFunc;
    panel = selectPanel;
    bottomPanel = otherPanel;
    itemWidthGap = itemWidth;
    itemheightGap = itemHeight;
    itemNode = node;
    clickCallFunc = clickFunc;
    moveCallFunc = moveFunc;

    -- 初始化当前选中位置
    selectIndex = 1;

    -- 初始化格子
    initGrid();

    -- 绘制一下
    redrawItems();

    -- 赋予点击事件
    selectPanel:addTouchEventListener(onPanelClick);
end

-- 子控件添加滑动
function childAddSlideMoveFunc(childItem)
    childItem:addTouchEventListener(onPanelClick);
end